// @HEADER
//*********************************************************************//
//  SiYuan: A numerical PDE solver                                     //
//  Copyright (2022) YUAN Xi                                           //
//  This Software is released under the BSD 2-Clause license detailed  //
//  in the file "LICENSE" in the top-level SiYuan directory            //
//*********************************************************************//
// @HEADER

#ifndef __XYZ_VALTREE_H
#define __XYZ_VALTREE_H

#include <vector>
#include <valarray>
#include <cmath>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cassert>

namespace XYZLib
{

template< typename evalT >
class valtree
{
    typedef std::valarray<evalT>   ValueType;

private:
    struct Node {
        typedef std::vector< std::unique_ptr<Node> >     Children;

        ValueType  _value;
        Children   _children;

        Node(const ValueType value): _value(value) {}
    };

    typedef typename Node::Children         Children;

private:
    std::unique_ptr<Node> m_root;
    size_t m_depth;

public:
	explicit valtree<evalT>() {
        m_depth = 0;
        m_root = nullptr;
    }
	
    explicit valtree<evalT>(const ValueType& value) {
        std::unique_ptr<Node> m_root(new Node(value));
        m_depth = 1;  
    }

    const bool empty() {return(m_root == nullptr);}
    size_t GetDepth() const {return m_depth;}

    /**
       * @brief Inserts a new node into the tree
    */
    const std::unique_ptr<Node> append(std::unique_ptr<Node> node, const ValueType& value) {
        std::unique_ptr<Node> child(new Node(value));
        if( node->_children->empty() ) m_depth++;
        node->_children.emplace_back(child);
        return child;
    }

    void print() const {print(m_root);}

    void print(std::unique_ptr<Node> node) const {
        if( node==nullptr ) return;
        if(node->_children!=nullptr) {
            size_t index=-1;
            for( const auto& e : node->_children ) { 
                std::cout << node->_value[index++] << std::endl;
                print( e );
            }
        } else {
            for( const auto& e : node->_value ) {
                std::cout << ":" << e;
            }
            std::cout << std::endl;
        }
    }

    void GetValue(const std::initializer_list<evalT> &args) {
        assert(args.size() == this->m_depth);
        GetValue(m_root, args);
    }

    void GetValue(std::unique_ptr<Node> node, const std::initializer_list<evalT> &args) {
        auto it = std::lower_bound(ALL(node->_value), args[0]);
        if( args[0]-node->_value[it] < std::numeric_limits<evalT>::epsilon() ) {}
    };

};

}

#endif
